//*************************************************************************************
//* 作    者： 
//* 创建时间： 2021-09-04 12:05:36
//* 描    述： 

//* ************************************************************************************
using UnityEngine;
using System.Collections.Generic;
using UFrame.State;

public class FlyingBase : UnityEngine.MonoBehaviour
{
    [SerializeField]
    protected Vector3 m_zeroPos;
    [SerializeField]
    protected float m_zRange = 10;
    [SerializeField]
    protected float m_speed = 20;
    [SerializeField]
    protected Animation m_animation;
    [SerializeField]
    protected Rect m_moveRect = new Rect(-20, -20, 40, 20);
    protected Quaternion m_targetRotation;
    protected Quaternion m_changeStart;
    protected int m_state;
    protected float m_directChangeAngle = 10;//按配制重设
    protected float m_directChangeTime = 3f;//按配制重设
    protected float m_directChangeTimeOutBound = 1f;//按配制重设
    protected float m_followTime = 5f;
    protected float m_moveTimer;//单次移动计数
    protected bool m_outBound;
    [SerializeField]
    protected float m_seekRange = 2;
    protected float m_eatDistance = 1;
    protected int m_effectCurrent = 1;
    protected Dictionary<int, int> m_baitLove = new Dictionary<int, int>();
    protected HashSet<int> m_ignore = new HashSet<int>();
    [SerializeField]
    protected string m_swimAnimName = "swim";
    [SerializeField]
    protected string m_struggleAnimName = "struggle";
    [SerializeField]
    protected string m_eatAnimName = "eat";
    [SerializeField]
    protected int m_stuggleAudio = 114;
    protected Rigidbody m_rigidbody;
    [SerializeField]
    protected float m_scaleFactor = 1;
    protected float m_instanceSpeed = 1;
    private float _rand1;
    private float _rand2;
    protected float m_viewScale = 1;
    protected float m_struggleSpeedMuti = 3;
    protected float m_goingSpeedMuti = 1.1f;
    [SerializeField]
    protected int m_bubbleId = 11;
    protected Vector3 m_dialogPos = new Vector3(0, 1f, 0.7f);
    protected bool m_foodEated = false;
    public float m_beginTimer = 3;//一个时间内强制不能咬钩
    public int State => m_state;
    protected EventStateCtrl<int> m_stateCtrl = new EventStateCtrl<int>((x, y) => x != y);
    //状态切换时，动画文件还控制着模型坐标，下延迟才能获得对的坐标！！！
    protected bool m_delySwitchHookParent = false;
    protected int m_delyFrame = 0;
    protected Collider m_collider;
    [SerializeField]
    public UnityEngine.Events.UnityEvent onSwim;

    private void Awake()
    {
        m_collider = GetComponent<Collider>();
        m_rigidbody = GetComponent<Rigidbody>();
        _rand1 = Random.Range(0.5f, 1.5f);
        _rand2 = Random.Range(0.5f, 1.5f);
        m_stateCtrl.SetStateChange(OnStateChanged);
    }

    private void OnStateChanged(int state)
    {
        if (m_rigidbody)
            m_rigidbody.isKinematic = state != FlyingState.Moving;
    }

    // Start is called before the first frame update
    private void Start()
    {
        InitStates();
        RandomSpan();
        m_state = FlyingState.Idle;
    }

    private void Update()
    {
        m_stateCtrl.UpdateState(m_state);
    }

    protected virtual void InitStates()
    {
        //m_stateCtrl.SetUpdateEvent(FlyingState.Idle, OnUpdateIdle);

        //m_stateCtrl.SetEnterEvent(FlyingState.Moving, OnEnterMoving);
        //m_stateCtrl.SetUpdateEvent(FlyingState.Moving, UpdateMoving);

        //m_stateCtrl.SetEnterEvent(FlyingState.Going, OnEnterGoing);
        //m_stateCtrl.SetUpdateEvent(FlyingState.Going, UpdateGoing);

        //m_stateCtrl.SetEnterEvent(FlyingState.Baiting, OnEnterBaiting);
        //m_stateCtrl.SetUpdateEvent(FlyingState.Baiting, UpdateBaiting);

        //m_stateCtrl.SetEnterEvent(FlyingState.Struggle, OnEnterStruggle);
        //m_stateCtrl.SetUpdateEvent(FlyingState.Struggle, OnUpdateStruggle);
        //m_stateCtrl.SetExitEvent(FlyingState.Struggle, OnExitStruggle);

        //m_stateCtrl.SetEnterEvent(FlyingState.NoPower, OnEnterNoPower);
        //m_stateCtrl.SetUpdateEvent(FlyingState.NoPower, OnUpdateNoPower);
        //m_stateCtrl.SetExitEvent(FlyingState.NoPower, OnExitNoPower);


        //m_stateCtrl.SetEnterEvent(FlyingState.Escape, OnEnterEscape);
        //m_stateCtrl.SetUpdateEvent(FlyingState.Escape, OnUpdateEscape);

        //m_stateCtrl.SetEnterEvent(FlyingState.Follow, OnEnterFollow);
        //m_stateCtrl.SetUpdateEvent(FlyingState.Follow, UpdateFollow);

        m_stateCtrl.SetEnterEvent(FlyingState.Die, OnEnterDie);
    }

    public bool IsPlayingStruggle()
    {
        if (m_animation)
            return m_animation.IsPlaying(m_struggleAnimName);
        return false;
    }

    public void PlaySwimAnim()
    {
        if (m_animation)
            m_animation.Play(m_swimAnimName);
    }

    private void OnEnterNoPower(FlyingState state)
    {

    }

    private FlyingState OnUpdateNoPower(FlyingState state)
    {
        return state;
    }

    private void OnExitNoPower(FlyingState state)
    {
    }

    private void OnEnterStruggle(FlyingState state)
    {
    }

    private FlyingState OnUpdateStruggle(FlyingState state)
    {
        return state;
    }

    private void OnExitStruggle(FlyingState state)
    {

    }

    private void OnUpdateIdle(FlyingState state)
    {
        //两位视野裁切
        SelectNextMoveDirection();
        m_state = FlyingState.Moving;
    }

    private void OnEnterFollow(FlyingState state)
    {
    }

    private void OnEnterEscape(FlyingState state)
    {
        if (m_animation)
            m_animation.Play(m_swimAnimName);
        transform.forward = -transform.forward;
    }

    private void OnEnterGoing(int state)
    {
        m_instanceSpeed = m_speed * m_goingSpeedMuti;
        m_foodEated = false;
    }

    private void OnEnterDie(int state)
    {

    }

    public void SetDeath()
    {
        m_state = FlyingState.Die;
    }

    private void OnEnterMoving(FlyingState state)
    {
        if (m_collider)
            m_collider.enabled = true;
        float noise = Mathf.PerlinNoise(Time.time * _rand1, Time.time * _rand2);
        m_instanceSpeed = noise * m_speed;
        if (m_animation)
            m_animation.Play(m_swimAnimName);
        if (onSwim != null)
            onSwim.Invoke();
    }

    private void OnUpdateEscape(int state)
    {
        m_moveTimer += Time.deltaTime;

        if (!m_outBound)
        {
            InsideMove(ref state);
            m_state = state;
        }
        else
        {
            OutSideMove();
        }
    }

    protected void UpdateMoving(int state)
    {
        m_moveTimer += Time.deltaTime;

        if (!m_outBound)
        {
            InsideMove(ref state);
            m_state = state;
        }
        else
        {
            OutSideMove();
        }
    }

    public void SetFollowTarget(Vector3 postion, float time)
    {
        if (m_state == FlyingState.Moving)
        {
            m_state = FlyingState.Follow;
            m_changeStart = transform.rotation;
            m_targetRotation = Quaternion.LookRotation(postion - transform.position);
            m_moveTimer = 0;
            m_followTime = time;
        }
    }

    protected void UpdateFollow(FlyingState state)
    {
        m_moveTimer += Time.deltaTime;
        transform.position += transform.forward * m_speed * Time.deltaTime;

        if (m_moveTimer > m_followTime)
        {
            m_moveTimer = 0;
            m_state = FlyingState.Moving;
        }
        else if (m_moveTimer < m_directChangeTime)
        {
            var retio = m_moveTimer / m_directChangeTime;
            transform.rotation = Quaternion.Lerp(m_changeStart, m_targetRotation, retio);
        }
    }

    protected FlyingState UpdateGoing(FlyingState state)
    {
        return state;
    }

    private void OnEnterBaiting(FlyingState state)
    {

    }

    protected void UpdateBaiting(FlyingState state)
    {
        m_state = FlyingState.NoPower;
    }
    protected void SelectNextMoveDirection()
    {
        var horiAngle = Random.Range(-m_directChangeAngle, m_directChangeAngle);
        var forward = Quaternion.AngleAxis(horiAngle, Vector3.up) * transform.forward;
        var zRange = Mathf.Abs(0.5f * forward.x);
        var yRange = Mathf.Abs(0.2f * forward.x);
        forward.y = Mathf.Clamp(forward.z, -yRange, yRange);
        forward.z = Mathf.Clamp(forward.z, -zRange, zRange);
        if (forward != Vector3.zero)
        {
            m_targetRotation = Quaternion.LookRotation(forward);
        }
        else
        {
            m_targetRotation = Quaternion.Euler(UnityEngine.Random.insideUnitSphere);
        }
        m_changeStart = transform.rotation;
    }

    // 边界处移动朝向调整
    protected void CheckBoundary()
    {
        var outBound = false;
        var direction = transform.forward;
        var transpos = transform.localPosition - m_zeroPos;
        if (transpos.z > m_zRange * 0.5f)
        {
            direction.z = -Mathf.Abs(direction.z);
            outBound = true;
        }
        else if (transpos.z < -m_zRange * 0.5f)
        {
            direction.z = Mathf.Abs(direction.z);
            outBound = true;
        }
        if (transpos.x < m_moveRect.x)
        {
            direction.x = Mathf.Abs(direction.x);
            outBound = true;
        }
        else if (transpos.x > m_moveRect.x + m_moveRect.width)
        {
            direction.x = -Mathf.Abs(direction.x);
            outBound = true;
        }
        if (transpos.y < m_moveRect.y)
        {
            direction.y = Mathf.Abs(direction.y);
            outBound = true;
        }
        else if (transpos.y > m_moveRect.y + m_moveRect.height)
        {
            direction.y = -Mathf.Abs(direction.y);
            outBound = true;
        }

        if (m_outBound != outBound)
        {
            m_moveTimer = 0;
            m_outBound = outBound;
            m_changeStart = transform.rotation;
            m_targetRotation = Quaternion.LookRotation(direction);
        }
    }

    protected void InsideMove(ref int state)
    {
        if (m_moveTimer > m_directChangeTime)
        {
            m_moveTimer = 0;
            state = FlyingState.Idle;
        }
        else
        {
            MoveAndCheck();
            var retio = m_moveTimer / m_directChangeTime;
            transform.rotation = Quaternion.Lerp(m_changeStart, m_targetRotation, retio);
        }
    }

    protected void MoveAndCheck(bool check = true)
    {
        if (m_rigidbody && !m_rigidbody.isKinematic)
        {
            m_rigidbody.velocity = transform.forward.normalized * m_instanceSpeed * Time.deltaTime * 100;
        }
        else
        {
            transform.position += transform.forward.normalized * m_instanceSpeed * Time.deltaTime;
        }

        if (check)
        {
            CheckBoundary();
        }
    }

    protected void OutSideMove()
    {
        if (m_moveTimer > m_directChangeTimeOutBound)
        {
            m_moveTimer = 0;
            m_outBound = false;
            CheckBoundary();
        }
        else
        {
            MoveAndCheck();
            var retio = m_moveTimer / m_directChangeTimeOutBound;
            transform.rotation = Quaternion.Lerp(m_changeStart, m_targetRotation, retio);
        }
    }

    protected void RandomSpan()
    {
        Vector3 pos = Vector3.zero;
        pos.x = Random.Range(m_moveRect.x, m_moveRect.x + m_moveRect.width);
        pos.y = Random.Range(m_moveRect.y, m_moveRect.y + m_moveRect.height);
        pos.z = Random.Range(0, m_zRange) - 0.5f * m_zRange;
        transform.localPosition = pos + m_zeroPos;
        transform.rotation = m_targetRotation;
    }
}

public class FlyingState
{
    public const int None = 0;
    public const int Idle = 1;
    public const int Moving = 2;
    public const int Going = 3;
    public const int Baiting = 4;
    public const int Struggle = 5;
    public const int NoPower = 6;
    public const int Escape = 7;
    public const int Follow = 8;
    public const int Die = 9;
}